package com.liuyk.commonlib.web;

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.webkit.DownloadListener;
import android.webkit.GeolocationPermissions;
import android.webkit.JsPromptResult;
import android.webkit.SslErrorHandler;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;

import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.Target;
import com.google.gson.JsonObject;
import com.liuyk.commonlib.BuildConfig;
import com.liuyk.commonlib.WebViewFragment;
import com.liuyk.commonlib.utility.CommonIntentUtils;
import com.liuyk.commonlib.utility.JsonUtils;
import com.liuyk.commonlib.web.bridge.BaseBridge;
import com.liuyk.commonlib.web.bridge.BasisView;
import com.liuyk.commonlib.web.entity.MethodData;

import java.io.File;
import java.io.IOException;
import java.util.Set;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;

/**
 * 封装WebView
 * <p>
 * Created by liuyk on 2019/4/15.
 */
public class APPWebView extends WebView {
    private static final String JAVASCRIPT = "javascript:";

    protected static final int PERMISSIONS_CAMERA = 10001;
    //录制视频
    protected static final int PERMISSIONS_CAMERA_VIDEO = 10002;
    /**
     * WebView使用指南
     * <p>
     * 1. Javascript相关
     * webSettings.setJavaScriptEnabled(true);
     * <p>
     * 2. 定位相关
     * 开启 DOM storage API 功能
     * webSettings.setDomStorageEnabled(true);
     * <p>
     * //启用地理定位
     * webSettings.setGeolocationEnabled(true);
     * <p>
     * 回调 onGeolocationPermissionsShowPrompt
     * <p>
     * 只能支持https，不支持http
     * <p>
     * 3. 拍照相关
     * <p>
     * 回调onShowFileChooser
     * //图片
     * String[] acceptTypes = fileChooserParams.getAcceptTypes();
     * if (acceptTypes != null && acceptTypes.length > 0) {
     * if (acceptTypes[0].contains("image")) {
     * captureImage();
     * return true;
     * }
     * }
     * <p>
     * 4. 视频相关
     * 回调onShowFileChooser
     * //视频
     * String[] acceptTypes = fileChooserParams.getAcceptTypes();
     * if (acceptTypes != null && acceptTypes.length > 0) {
     * if (acceptTypes[0].contains("video")) {
     * captureImage();
     * return true;
     * }
     * <p>
     * 5.下载相关
     * mWebView.setDownloadListener(new DownloadListener() {
     * <p>
     * public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
     * Uri uri = Uri.parse(url);
     * Intent intent = new Intent(Intent.ACTION_VIEW, uri);
     * listener.startActivity(intent);
     * }
     * });
     * // webview 长按保存图片
     * mWebView.setOnLongClickListener(new View.OnLongClickListener() {
     * public boolean onLongClick(View v) {
     */

    private WebViewFragment mWebViewFragment;

    private Handler mHandler = new Handler(Looper.getMainLooper());
    private ValueCallback<Uri[]> uploadMessageAboveL;

    //拍照
    private File imgFile;
    private File mVideoFile;

    private BasisView mBasisView;

    public void setBasisView(BasisView basisView) {
        mBasisView = basisView;
    }

    public APPWebView(Context context) {
        this(context, null);
    }

    public APPWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        setWebContentsDebuggingEnabled(BuildConfig.DEBUG);
        setWebChromeClient(new MyWebChromeClient());
        setWebViewClient(new MyWebViewClient());
        WebSettings webSettings = getSettings();
        webSettings.setSupportZoom(true);//支持手势缩放
        webSettings.setBuiltInZoomControls(true);//支持手势缩放
        webSettings.setDisplayZoomControls(false);//不显示缩放按钮
        //自适应屏幕
        webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
        //设置Web页面字体稳定大小
        webSettings.setTextZoom(100);
        /**
         * 用WebView显示图片，可使用这个参数 设置网页布局类型：
         * 1、LayoutAlgorithm.NARROW_COLUMNS ：适应内容大小
         * 2、LayoutAlgorithm.SINGLE_COLUMN : 适应屏幕，内容将自动缩放
         */
        webSettings.setBlockNetworkImage(false);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
        }
        //文件访问漏洞处理
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            webSettings.setAllowFileAccessFromFileURLs(false);
            webSettings.setAllowUniversalAccessFromFileURLs(false);
        }
        webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
        //设置加载进来的页面自适应手机屏幕
        webSettings.setUseWideViewPort(true);
        webSettings.setLoadWithOverviewMode(true);
        webSettings.setJavaScriptEnabled(true);

//        setInitialScale(100);
        //启用地理定位
        webSettings.setGeolocationEnabled(true);
        // 开启 DOM storage API 功能
        webSettings.setDomStorageEnabled(true);
        //开启 database storage API 功能
        webSettings.setDatabaseEnabled(true);

        //长按保存图片
        setOnLongClickListener(new OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                final HitTestResult hitTestResult = getHitTestResult();
                if (hitTestResult == null) {
                    return false;
                }
                int type = hitTestResult.getType();
                if (HitTestResult.IMAGE_TYPE == type || HitTestResult.SRC_IMAGE_ANCHOR_TYPE == type) {

                    new AlertDialog.Builder(v.getContext())
                            .setTitle("保存图片")
                            .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    downloadImage(hitTestResult.getExtra());
                                }
                            })
                            .setNegativeButton("取消", null)
                            .show();
                }
                return false;
            }
        });

        //下载
        setDownloadListener(new DownloadListener() {
            @Override
            public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
                Uri uri = Uri.parse(url);
                Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                getContext().startActivity(intent);
            }
        });
    }

    /**
     * 下载图片到本地
     */
    private void downloadImage(@NonNull String url) {
        if (!TextUtils.isEmpty(url)) {
            final Uri uri = Uri.parse(url);
            final String scheme = uri.getScheme();
            if ("http".equals(scheme) || "https".equals(scheme)) {
//                if (checkWrite()) {
//                }
                exeDownloadImage(url);
            }
        }
    }

    private void exeDownloadImage(final String url) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    File file = Glide.with(getContext())
                            .load(url)
                            .downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
                            .get();

                    updateToSystemAlbum(file);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private void updateToSystemAlbum(File imageFile) {
        if (null == imageFile) {
            return;
        }
        String imagePath = imageFile.getAbsolutePath();
        String imageName = imageFile.getName();
        try {
            MediaStore.Images.Media.insertImage(getContext().getContentResolver(), imagePath, imageName, null);
            saveImgSuccess();
        } catch (Exception e) {
            e.printStackTrace();
            saveImgFail();
        }
        //第二个参数要是 Uri.parse("file://" + imagePath); 其他格式的uri无效
        getContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + imagePath)));
    }

    private void saveImgSuccess() {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getContext(), "保存成功", Toast.LENGTH_LONG).show();
            }
        });
    }

    private void saveImgFail() {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getContext(), "保存失败", Toast.LENGTH_LONG).show();
            }
        });
    }

    private void captureImage() {
        imgFile = createVideoFile(".jpg");

        Uri imageUri;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            imageUri = FileProvider.getUriForFile(getContext(), getContext().getPackageName() + ".fileprovider", imgFile);//通过FileProvider创建一个content类型的Uri
        } else {
            imageUri = Uri.fromFile(imgFile);
        }
        final Intent intent = new Intent();
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //加入这一句表示对目标应用暂时授权该Uri所代表的文件
        intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//设置Action为拍照
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//将拍取的照片保存到指定URI
        if (mWebViewFragment != null) {
            mWebViewFragment.startActivityForResult(intent, 1001);
        } else {
            ((Activity) getContext()).startActivityForResult(intent, 1001);
        }
    }

    /**
     * 启动相机，创建文件，并要求返回uri
     */
    private void takeVideo() {
        final Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
        //指定动作，启动相机
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        //创建文件
        mVideoFile = createVideoFile(".mp4");
        //添加临时权限
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        Uri mVideoUri;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            //做一些处理
            //获取uri
            mVideoUri = FileProvider.getUriForFile(getContext(), getContext().getPackageName() + ".fileprovider", mVideoFile);
        } else {
            //在版本低于此的时候，做一些处理
            mVideoUri = Uri.fromFile(mVideoFile);
        }

        //将uri加入到额外数据
        intent.putExtra(MediaStore.EXTRA_OUTPUT, mVideoUri);
        //启动相机并要求返回结果
        if (mWebViewFragment != null) {
            mWebViewFragment.startActivityForResult(intent, 1002);
        } else {
            ((Activity) getContext()).startActivityForResult(intent, 1002);
        }
    }

    /**
     * 创建视频文件
     */
    private File createVideoFile(String suffix) {
        //不加这句会报Read-only警告。且无法写入SD
//        mVideoFile.setWritable(true);
        File temp = new File(Environment.getExternalStorageDirectory(), "temp");
        if (!temp.exists()) {
            temp.mkdirs();
        }
        File imgFile = new File(temp, System.currentTimeMillis() + suffix);
        if (!imgFile.exists()) {
            try {
                imgFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return imgFile;
    }

    protected void requestCameraPermissions(int permissionsType) {
        String[] permissions = {Manifest.permission.CAMERA};
        if (mWebViewFragment != null) {
            mWebViewFragment.requestPermissions(permissions, permissionsType);
        } else {
            ActivityCompat.requestPermissions((Activity) getContext(), permissions, permissionsType);
        }
    }

    private boolean isCameraPermissions() {
        String[] permissions = {Manifest.permission.CAMERA};
        if (!ActivityCompat.shouldShowRequestPermissionRationale((Activity) getContext(), permissions[0])) {
            //用户拒绝，并且勾了不再提示，这时候只能跳转到设置页
            CommonIntentUtils.goToPermissionsSettingPager(getContext());
            return false;
        }
        int permissionCheck = ContextCompat.checkSelfPermission(getContext(), permissions[0]);
        return permissionCheck == PackageManager.PERMISSION_GRANTED;
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        //拍照
        if (requestCode == 1001) {
            uploadMessageAboveL.onReceiveValue(new Uri[]{Uri.fromFile(imgFile)});
            return;
        }
        //视频
        if (requestCode == 1002) {
            uploadMessageAboveL.onReceiveValue(new Uri[]{Uri.fromFile(mVideoFile)});
        }
    }

    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            //拍照
            case PERMISSIONS_CAMERA:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //允许权限
                    captureImage();
                } else {
                    cancelUpload();
                }
                break;

            //录制视频
            case PERMISSIONS_CAMERA_VIDEO:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //允许权限
                    takeVideo();
                } else {
                    cancelUpload();
                }
                break;
        }
    }

    private void cancelUpload() {
        if (uploadMessageAboveL != null) {
            uploadMessageAboveL.onReceiveValue(null);
        }
    }

    private final class MyWebChromeClient extends WebChromeClient {
        @Override
        public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
            super.onGeolocationPermissionsShowPrompt(origin, callback);
        }

        @Override
        public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
            uploadMessageAboveL = filePathCallback;
            //只能支持5.0及以上
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                return false;
            }

            String[] acceptTypes = fileChooserParams.getAcceptTypes();
            if (acceptTypes != null && acceptTypes.length > 0) {
                //图片
                if (acceptTypes[0].contains("image")) {
                    if (isCameraPermissions()) {
                        captureImage();
                    } else {
                        requestCameraPermissions(PERMISSIONS_CAMERA);
                    }
                    return true;
                }

                //视频
                if (acceptTypes[0].contains("video")) {
                    if (isCameraPermissions()) {
                        takeVideo();
                    } else {
                        requestCameraPermissions(PERMISSIONS_CAMERA_VIDEO);
                    }
                    return true;
                }
            }
            return false;
        }

        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            super.onProgressChanged(view, newProgress);
            mBasisView.onProgressChanged(newProgress, view.getUrl());
        }

//        @Override
//        public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
//            result.confirm();
//            return true;
//        }


        @Override
        public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
                                  JsPromptResult result) {
            try {
                MethodData methodData = JsonUtils.parseJson(MethodData.class, message);
                if (methodData == null) {
                    return true;
                }
                String params = defaultValue;
                if (TextUtils.isEmpty(defaultValue)) {
                    params = "{}";
                }
                methodData.params = params;
                String bridgeName = methodData.bridgeName;
                String action = methodData.action;
                String extra = methodData.extra;
                String callbackFunction = methodData.callbackFunction;
                boolean isSync = !TextUtils.isEmpty(callbackFunction);

                BaseBridge baseBridge = BridgeManager.getInstance().getBridge(bridgeName);
                String resultMsg;
                if (null != baseBridge) {
                    resultMsg = baseBridge.handle(action, isSync, extra,
                            callbackFunction, params, mBasisView);
                } else {
                    resultMsg = mBasisView.onExtraBridge(bridgeName, action, isSync,
                            extra, callbackFunction, params);
                }
                result.confirm(resultMsg);
            } catch (Exception e) {
                result.confirm(null);
            }
            return true;
        }
    }

    public final class MyWebViewClient extends WebViewClient {

        //返回值是true的时候控制去WebView打开，为false系统默认处理，一般都是重新load一次，也会发生load多次的情况
        //如果遇到重定向的url，会加载失败
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            //eg: url = "js://webview?arg1=111&arg2=222"
            Uri uri = Uri.parse(url);
            if ("js".equals(uri.getScheme())) {
                if ("webview".equals(uri.getAuthority())) {

                    try {
                        JsonObject jo = new JsonObject();
                        Set<String> collection = uri.getQueryParameterNames();

                        for (String s : collection) {
                            String queryParameter = uri.getQueryParameter(s);
                            jo.addProperty(s, queryParameter);
                        }

                        MethodData methodData = JsonUtils.parseJson(MethodData.class,
                                jo.toString());
                        if (methodData == null) {
                            return super.shouldOverrideUrlLoading(view, url);
                        }
                        String callbackFunction = methodData.callbackFunction;
                        boolean isSync = !TextUtils.isEmpty(callbackFunction);

                        BaseBridge baseBridge = BridgeManager.getInstance().
                                getBridge(methodData.bridgeName);
                        if (null != baseBridge) {
                            baseBridge.handle(
                                    methodData.action,
                                    isSync,
                                    methodData.extra,
                                    callbackFunction,
                                    methodData.params, mBasisView);
                        } else {
                            mBasisView.onExtraBridge(
                                    methodData.bridgeName, methodData.action, isSync,
                                    methodData.extra, callbackFunction,
                                    methodData.params);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                return true;
            }
            return super.shouldOverrideUrlLoading(view, url);
        }

        @Override
        public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
            super.onReceivedError(view, request, error);
        }

        @Override
        public void onPageFinished(final WebView view, final String url) {
            // 隐藏loading，显示其他视图
            if (!url.startsWith("js")) {
                mBasisView.setTitle(view.getTitle());
            }
            super.onPageFinished(view, url);
        }

        @Override
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
            handler.proceed();//忽略所有的证书提醒，不可以调用super
        }
    }

    public void evaluateJavascript(String callbackFunction,
                                   String response,
                                   ValueCallback<String> resultCallback) {
        if (TextUtils.isEmpty(callbackFunction)) {
            return;
        }

        response = parse(response);

        try {
            String jsFun = getJsFun(callbackFunction, response);
            evaluateJavascript(jsFun, resultCallback);
        } catch (Exception e) {
            // loadUrl会进行decode，所以先将response进行decode一次
            response = Uri.encode(response);
            String jsFun = getJsFun(callbackFunction, response);
            loadUrl(jsFun);
        }
    }

    // 获取jsFun
    private String getJsFun(String callbackFunction, String response) {
        //javascript:login('null','');
        //String jsFun = JAVASCRIPT + callbackFunction + "(\'" + callbackId + "\'," + "\'" + response + "\');";
        return JAVASCRIPT + callbackFunction + "(\'" + response + "\')";
    }

    // 转义response
    private String parse(String response) {
        if (TextUtils.isEmpty(response)) {
            return "";
        }
        response = response.replace("\\", "\\\\");
        response = response.replace("\"", "\\\"");
        response = response.replace("\'", "\\\'");
        response = response.replace("\n", "\\n");
        response = response.replace("\r", "\\r");
        response = response.replace("\f", "\\f");
        response = response.replace("\u2028", "\\u2028");
        response = response.replace("\u2029", "\\u2029");
        return response;
    }

    public void setWebViewFragment(WebViewFragment mWebViewFragment) {
        this.mWebViewFragment = mWebViewFragment;
    }
}
